package com.bzframework.project.picture.service;

import com.bzframework.pro.authorize.token.UserToken;
import com.bzframework.project.picture.entity.PictureEntity;
import com.bzframework.project.picture.enums.ReviewStatusEnum;
import com.bzframework.project.picture.manager.PictureFileManager;
import com.bzframework.project.picture.model.request.PictureReviewRequest;
import lombok.RequiredArgsConstructor;
import org.hswebframework.ezorm.rdb.mapping.defaults.SaveResult;
import org.hswebframework.web.crud.service.GenericReactiveCrudService;
import org.hswebframework.web.exception.BusinessException;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.codec.multipart.FilePart;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.util.unit.DataSize;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

import java.time.Duration;
import java.util.Arrays;
import java.util.List;

/**
 * @Author bingzhong
 * @Date 2025/1/15 17:19
 * @Description
 */
@Service
@RequiredArgsConstructor
public class PictureService extends GenericReactiveCrudService<PictureEntity, String> {

    private final PictureFileManager pictureFileManager;

    final List<String> ALLOW_CONTENT_TYPES = Arrays.asList("jpeg", "jpg", "png", "webp");

    public Mono<PictureEntity> uploadPicture(FilePart filePart, PictureEntity picture, UserToken userToken) {

        String pictureId = picture.getId();
        String uploadPathPrefix = String.format("picture/%s", userToken.getUserId());
        // 更新判断图片是否存在
        if (StringUtils.hasText(pictureId)) {
            return this.findById(pictureId)
                       .switchIfEmpty(Mono.error(new BusinessException("图片不存在")))
                       .then(updateOrInsertPicture(filePart, userToken, pictureId, uploadPathPrefix));
        }
        return updateOrInsertPicture(filePart, userToken, pictureId, uploadPathPrefix);

    }

    /**
     * 审核图片
     *
     * @param request 审核请求
     */
    public Mono<SaveResult> doReviewPicture(PictureReviewRequest request, UserToken userToken) {

        String pictureId = request.getId();
        ReviewStatusEnum reviewStatus = request.getReviewStatus();
        return this.findById(pictureId)
                   .switchIfEmpty(Mono.error(new BusinessException("图片不存在")))
                   .flatMap(pictureEntity -> {
                       ReviewStatusEnum oldReviewStatus = pictureEntity.getReviewStatus();
                       if (oldReviewStatus == reviewStatus) {
                           return Mono.error(new BusinessException("请勿重复设置图片状态"));
                       }
                       return Mono.just(pictureEntity);
                   })
                   .flatMap(pictureEntity -> {
                       pictureEntity.setReviewId(userToken.getUserId());
                       pictureEntity.setReviewTime(System.currentTimeMillis());
                       pictureEntity.setReviewStatus(reviewStatus);
                       pictureEntity.setReviewMessage(request.getReviewMessage());
                       return this.save(pictureEntity);
                   });

    }

    public Mono<Boolean> uploadPictureByUrl(UserToken userToken, String fileUrl) {

        return checkPictureLegitimate(fileUrl);
    }

    private Mono<Boolean> checkPictureLegitimate(String fileUrl) {
        return WebClient.create(fileUrl)
                        .head()
                        .exchangeToMono(clientResponse -> {
                            ClientResponse.Headers headers = clientResponse.headers();
                            String contentType = headers
                                    .contentType()
                                    .orElseThrow(() -> new BusinessException("content-type不存在"))
                                    .getSubtype();
                            if (StringUtils.hasText(contentType)) {
                                // 允许的图片类型
                                if (!ALLOW_CONTENT_TYPES.contains(contentType.toLowerCase())) {
                                    return Mono.error(new BusinessException("图片类型不支持"));
                                }
                            }
                            // 检查图片大小
                            long contentLength = headers
                                    .contentLength()
                                    .orElseThrow(() -> new BusinessException("content-type不存在"));
                            long megabytes = DataSize.ofMegabytes(2).toBytes();
                            if (contentLength > megabytes) {
                                return Mono.error(new BusinessException("图片大小不能超过2M"));
                            }
                            return Mono.just(true);
                        });
    }

    private Mono<PictureEntity> updateOrInsertPicture(FilePart filePart, UserToken userToken, String pictureId, String uploadPathPrefix) {
        return pictureFileManager.uploadFile(filePart, uploadPathPrefix)
                                 .flatMap(fileInfo -> {
                                     PictureEntity pictureEntity = new PictureEntity();
                                     pictureEntity.setId(pictureId);
                                     pictureEntity.setUrl(fileInfo.getUrl());
                                     pictureEntity.setName(fileInfo.getName());
                                     pictureEntity.setPicSize(fileInfo.getSize());
                                     pictureEntity.setPicWidth(fileInfo.getWidth());
                                     pictureEntity.setPicHeight(fileInfo.getHeight());
                                     pictureEntity.setPicScale(fileInfo.getScale());
                                     pictureEntity.setPicFormat(fileInfo.getFormat());
                                     pictureEntity.setCreatorId(userToken.getUserId());
                                     pictureEntity.setCreateTime(System.currentTimeMillis());
                                     pictureEntity.setModifierId(userToken.getUserId());
                                     pictureEntity.setModifyTime(System.currentTimeMillis());
                                     return this.save(pictureEntity).thenReturn(pictureEntity);
                                 });
    }
}
